#ifdef __STDC_VERSION__
#if __STDC_VERSION__ < 199901L
#error Rok 1999 już dawno za nami. Czas uaktualnić kompilator!
#endif
#else
#error Rok 1999 już dawno za nami. Czas uaktualnić kompilator!
#endif

#include<stdint.h>
#include<stdbool.h>
#include"przeksztalcenia.h"

/* Przekształcenie danych początkowe w szyfrowaniu i końcowe w deszyfrowaniu */
#define dlugoscIP 64
const uint_fast8_t IP[dlugoscIP]={
   58, 50, 42, 34, 26, 18, 10, 2,
   60, 52, 44, 36, 28, 20, 12, 4,
   62, 54, 46, 38, 30, 22, 14, 6,
   64, 56, 48, 40, 32, 24, 16, 8,
   57, 49, 41, 33, 25, 17,  9, 1,
   59, 51, 43, 35, 27, 19, 11, 3, 
   61, 53, 45, 37, 29, 21, 13, 5, 
   63, 55, 47, 39, 31, 23, 15, 7,
};

/* Przekształcenie danych końcowe w szyfrowaniu i początkowe w deszyfrowaniu */
#define dlugoscPI 64
const uint_fast8_t PI[dlugoscPI]={
   40, 8, 48, 16, 56, 24, 64, 32,
   39, 7, 47, 15, 55, 23, 63, 31,
   38, 6, 46, 14, 54, 22, 62, 30,
   37, 5, 45, 13, 53, 21, 61, 29,
   36, 4, 44, 12, 52, 20, 60, 28,
   35, 3, 43, 11, 51, 19, 59, 27,
   34, 2, 42, 10, 50, 18, 58, 26,
   33, 1, 41,  9, 49, 17, 57, 25,
};

/* Przekształcenie danych rozszerzające 32 bity na 48 bitów */
#define dlugoscE 48
const uint_fast8_t E[dlugoscE]={
   32,  1,  2,  3,  4,  5,
    4,  5,  6,  7,  8,  9,
    8,  9, 10, 11, 12, 13,
   12, 13, 14, 15, 16, 17,
   16, 17, 18, 19, 20, 21,
   20, 21, 22, 23, 24, 25,
   24, 25, 26, 27, 28, 29,
   28, 29, 30, 31, 32,  1,
}; 

/* Przekształcenie danych po przejściu przez mieszanie za pomocą S bloków */
#define dlugoscP 32
const uint_fast8_t P[dlugoscP]={
   16,  7, 20, 21,
   29, 12, 28, 17,
    1, 15, 23, 26,
    5, 18, 31, 10,
    2,  8, 24, 14,
   32, 27,  3,  9,
   19, 13, 30,  6,
   22, 11,  4, 25,
};

/* Przekształcenie klucza początkowe */
#define dlugoscPC1 56
const uint_fast8_t PC1[dlugoscPC1]={
   57, 49, 41, 33, 25, 17,  9,
    1, 58, 50, 42, 34, 26, 18,
   10,  2, 59, 51, 43, 35, 27,
   19, 11,  3, 60, 52, 44, 36,
   63, 55, 47, 39, 31, 23, 15,
    7, 62, 54, 46, 38, 30, 22,
   14,  6, 61, 53, 45, 37, 29,
   21, 13,  5, 28, 20, 12,  4,
};

/* Przekształcenie klucza dla każdego kroku */
#define dlugoscPC2 48
const uint_fast8_t PC2[dlugoscPC2]={
   14, 17, 11, 24,  1,  5,
    3, 28, 15,  6, 21, 10,
   23, 19, 12,  4, 26,  8,
   16,  7, 27, 20, 13,  2,
   41, 52, 31, 37, 47, 55,
   30, 40, 51, 45, 33, 48,
   44, 49, 39, 56, 34, 53,
   46, 42, 50, 36, 29, 32,
};

/* Przesunięcie klucza w kolejnych krokach */
#define dlugoscPrzesuniecia 16
const bool Przesuniecia[dlugoscPrzesuniecia]={
   false, false,  true,  true,  true,  true,  true,  true,
   false,  true,  true,  true,  true,  true,  true, false,
};

/* S bloki */
#define dlugoscXS 16
#define dlugoscYS 4
#define dlugoscS 8

const uint_fast8_t S[dlugoscS][dlugoscYS][dlugoscXS]={
   {
      {14,  4, 13,  1,  2, 15, 11,  8,  3, 10,  6, 12,  5,  9,  0,  7,},
      { 0, 15,  7,  4, 14,  2, 13,  1, 10,  6, 12, 11,  9,  5,  3,  8,},
      { 4,  1, 14,  8, 13,  6,  2, 11, 15, 12,  9,  7,  3, 10,  5,  0,},
      {15, 12,  8,  2,  4,  9,  1,  7,  5, 11,  3, 14, 10,  0,  6, 13,},
   },
   {
      {15,  1,  8, 14,  6, 11,  3,  4,  9,  7,  2, 13, 12,  0,  5, 10,},
      { 3, 13,  4,  7, 15,  2,  8, 14, 12,  0,  1, 10,  6,  9, 11,  5,},
      { 0, 14,  7, 11, 10,  4, 13,  1,  5,  8, 12,  6,  9,  3,  2, 15,},
      {13,  8, 10,  1,  3, 15,  4,  2, 11,  6,  7, 12,  0,  5, 14,  9,},
   },
   {
      {10,  0,  9, 14,  6,  3, 15,  5,  1, 13, 12,  7, 11,  4,  2,  8,},
      {13,  7,  0,  9,  3,  4,  6, 10,  2,  8,  5, 14, 12, 11, 15,  1,},
      {13,  6,  4,  9,  8, 15,  3,  0, 11,  1,  2, 12,  5, 10, 14,  7,},
      { 1, 10, 13,  0,  6,  9,  8,  7,  4, 15, 14,  3, 11,  5,  2, 12,},
   },
   {
      { 7, 13, 14,  3,  0,  6,  9, 10,  1,  2,  8,  5, 11, 12,  4, 15,},
      {13,  8, 11,  5,  6, 15,  0,  3,  4,  7,  2, 12,  1, 10, 14,  9,},
      {10,  6,  9,  0, 12, 11,  7, 13, 15,  1,  3, 14,  5,  2,  8,  4,},
      { 3, 15,  0,  6, 10,  1, 13,  8,  9,  4,  5, 11, 12,  7,  2, 14,},
   },
   {
      { 2, 12,  4,  1,  7, 10, 11,  6,  8,  5,  3, 15, 13,  0, 14,  9,},
      {14, 11,  2, 12,  4,  7, 13,  1,  5,  0, 15, 10,  3,  9,  8,  6,},
      { 4,  2,  1, 11, 10, 13,  7,  8, 15,  9, 12,  5,  6,  3,  0, 14,},
      {11,  8, 12,  7,  1, 14,  2, 13,  6, 15,  0,  9, 10,  4,  5,  3,},
   },
   {
      {12,  1, 10, 15,  9,  2,  6,  8,  0, 13,  3,  4, 14,  7,  5, 11,},
      {10, 15,  4,  2,  7, 12,  9,  5,  6,  1, 13, 14,  0, 11,  3,  8,},
      { 9, 14, 15,  5,  2,  8, 12,  3,  7,  0,  4, 10,  1, 13, 11,  6,},
      { 4,  3,  2, 12,  9,  5, 15, 10, 11, 14,  1,  7,  6,  0,  8, 13,},
   },
   {
      { 4, 11,  2, 14, 15,  0,  8, 13,  3, 12,  9,  7,  5, 10,  6,  1,},
      {13,  0, 11,  7,  4,  9,  1, 10, 14,  3,  5, 12,  2, 15,  8,  6,},
      { 1,  4, 11, 13, 12,  3,  7, 14, 10, 15,  6,  8,  0,  5,  9,  2,},
      { 6, 11, 13,  8,  1,  4, 10,  7,  9,  5,  0, 15, 14,  2,  3, 12,},
   },
   {
      {13,  2,  8,  4,  6, 15, 11,  1, 10,  9,  3, 14,  5,  0, 12,  7,},
      { 1, 15, 13,  8, 10,  3,  7,  4, 12,  5,  6, 11,  0, 14,  9,  2,},
      { 7, 11,  4,  1,  9, 12, 14,  2,  0,  6, 10, 13, 15,  3,  5,  8,},
      { 2,  1, 14,  7,  4, 10,  8, 13, 15, 12,  9,  0,  3,  5,  6, 11,},
   },
};

/* Struktura oznaczająca parę (kluczy, danych) */
typedef struct
{
   uint_fast32_t L;
   uint_fast32_t P;
} para_t;

/* 
 * Funkcja odwracająca bity w słowie 64 bitowym.
 * Umożliwa szybką implementację na bazie operacji bitowych.
 */
static inline uint_fast64_t odwroc(uint_fast64_t wejscie)
{
   uint_fast64_t wyjscie=0ull;
   unsigned char i;
   for(i=0; i<64; ++i)
   {
      wyjscie <<= 1;
      wyjscie |= wejscie&1;
      wejscie >>= 1;
   }
   return wyjscie;
}

/* 
 * Funkcja odwracająca bity w połówce bajta.
 * Umożliwa szybką implementację na bazie operacji bitowych.
 */
static inline uint_fast8_t odwroc4(uint_fast8_t wejscie)
{
   uint_fast8_t wyjscie=0u;
   unsigned char i;
   for(i=0; i<4; ++i)
   {
      wyjscie <<= 1;
      wyjscie |= wejscie&1;
      wejscie >>= 1;
   }
   return wyjscie;
}
/* 
 * Funkcja obracająca słowo 32 bitowe.
 * Pierwszy bit przesuwa się na koniec słowa.
 */
static inline uint_fast32_t odobroc(uint_fast32_t wejscie)
{
   return ( (wejscie & 0x7FFFFFF)  << 1) | ( wejscie & 0x8000000 && 1 );
}

/* 
 * Funkcja obracająca słowo 32 bitowe.
 * Ostatni bit przesuwa się na początek słowa.
 */
static inline uint_fast32_t obroc(uint_fast32_t wejscie)
{
   return (wejscie >> 1) | ( (wejscie & 1) << 27 );
}

/* Funkcja obsługujące powyższe tablice przekształceń dla słów 64 bitowych */
static inline uint_fast64_t wybierz(
      unsigned char dlugosc,
      const unsigned char tablica[],
      uint_fast64_t wejscie
      )
{
   uint_fast64_t wyjscie=0ull;
   for(; dlugosc>0; --dlugosc)
   {
      wyjscie <<= 1;
      wyjscie |= ( ((uint_fast64_t)1ull << (tablica[dlugosc-1] - 1) ) & wejscie && 1);
   }
   return wyjscie;
}

/* Funkcja obsługujące powyższe tablice przekształceń dla słów 32 bitowych */
static inline uint_fast32_t wybierz32(
      unsigned char dlugosc,
      const unsigned char tablica[],
      uint_fast32_t wejscie
      )
{
   uint_fast32_t wyjscie=0ul;
   for(; dlugosc>0; --dlugosc)
   {
      wyjscie <<= 1;
      wyjscie |= ( (1 << (tablica[dlugosc-1] - 1) ) & wejscie && 1);
   }
   return wyjscie;
}

/* Dzieli słowo 64 bitowe na parę słów 32 bitowych */
static inline para_t podziel(uint_fast64_t wejscie)
{
   para_t wyjscie;
   wyjscie.L=wejscie & 0xFFFFFFFF;
   wyjscie.P=wejscie >> 32;
   return wyjscie;
}

/* Łączy parę słów 32 bitowych w słowo 64 bitowe */
static inline uint_fast64_t polacz(para_t wejscie)
{
   return (uint_fast64_t)wejscie.L | ((uint_fast64_t)wejscie.P << 32);
}

/* Dzieli klucz na dwie części po 28 bitów */
static inline para_t podziel_klucz(uint_fast64_t wejscie)
{
   para_t wyjscie;
   wyjscie.L=wejscie & 0xFFFFFFF;
   wyjscie.P=wejscie >> 28;
   return wyjscie;
}

/* Tworzy klucz z dwóch części po 28 bitów */
static inline uint_fast64_t polacz_klucz(para_t wejscie)
{
   return (uint_fast64_t)wejscie.L | ((uint_fast64_t)wejscie.P << 28);
}

/* Funkcja obsługująca pojedyńczy S blok */
static inline uint_fast32_t s(uint_fast64_t wejscie)
{
   uint_fast32_t wyjscie=0ul;
   unsigned char i;
   uint_fast8_t bloczek;
   unsigned char x;
   unsigned char y;
   for(i=0; i<8; ++i)
   {
      wyjscie >>= 4;
      bloczek = wejscie & 0x3F;
      wejscie >>= 6;
      x = ((bloczek & 0x2) << 2) |
	  (bloczek & 0x4) |
	  ((bloczek & 0x8) >> 2) |
	  ((bloczek & 0x10) >> 4);
      y = ((bloczek & 0x1) << 1) | ((bloczek & 0x20) >> 5);
      wyjscie |= (uint_fast32_t)odwroc4(S[i][y][x]) << 28;
   }
   return wyjscie;
}

/* Funkcja f ze specyfikacji algorytmu (FIPS 46-3). */
static inline uint_fast32_t f(uint_fast64_t klucz, uint_fast32_t wejscie)
{
   return wybierz32(dlugoscP,P,s(klucz ^ wybierz(dlugoscE,E,wejscie)));

}

/* Funckja szyfrująca przy pomocy algorytmu DES */
uint_fast64_t szyfruj_des(uint_fast64_t klucz, uint_fast64_t wejscie)
{
   unsigned char i;
   para_t bloki, stare_bloki, bloki_klucza;
   /* Odwracam kolejność bitów w słowach */
   klucz=odwroc(klucz);
   wejscie=odwroc(wejscie);

   klucz=wybierz(dlugoscPC1,PC1,klucz);
   bloki_klucza=podziel_klucz(klucz);

   /* Stosuję początkowe przekształcenie klucza */
   wejscie=wybierz(dlugoscIP,IP,wejscie);
   stare_bloki=podziel(wejscie);

   /* Główna pętla algorytmu */
   for(i=0; i<16; ++i)
   {
      bloki_klucza.L=obroc(bloki_klucza.L);
      bloki_klucza.P=obroc(bloki_klucza.P);
      /* Jeżeli trzeba obrócić klucz 2 razy... */
      if(Przesuniecia[i])
      {
	 bloki_klucza.L=obroc(bloki_klucza.L);
	 bloki_klucza.P=obroc(bloki_klucza.P);
      }
      klucz=wybierz(dlugoscPC2,PC2,polacz_klucz(bloki_klucza));

      /* Zamiana bloków danych */
      bloki.L=stare_bloki.P;
      bloki.P=stare_bloki.L ^ f(klucz,stare_bloki.P);

      stare_bloki=bloki;
   }
   /* Ostateczna zamiana bloków danych */
   bloki.L=stare_bloki.P;
   bloki.P=stare_bloki.L;
   /* Końcowe przekształcenie i odwrócenie bitów */
   return odwroc(wybierz(dlugoscPI,PI,polacz(bloki)));
}

/* Funckja deszyfrująca przy pomocy algorytmu DES */
uint_fast64_t deszyfruj_des(uint_fast64_t klucz, uint_fast64_t wejscie)
{
   signed char i;
   para_t bloki, stare_bloki, bloki_klucza;
   /* Odwracam kolejność bitów w słowach */
   klucz=odwroc(klucz);
   wejscie=odwroc(wejscie);

   /* Stosuję początkowe przekształcenie klucza */
   klucz=wybierz(dlugoscPC1,PC1,klucz);
   bloki_klucza=podziel_klucz(klucz);

   wejscie=wybierz(dlugoscIP,IP,wejscie);
   stare_bloki=podziel(wejscie);

   /* Główna pętla algorytmu */
   for(i=15; i>=0; --i)
   {
      klucz=wybierz(dlugoscPC2,PC2,polacz_klucz(bloki_klucza));

      bloki_klucza.L=odobroc(bloki_klucza.L);
      bloki_klucza.P=odobroc(bloki_klucza.P);
      /* Jeżeli trzeba obrócić klucz 2 razy... */
      if(Przesuniecia[i])
      {
	 bloki_klucza.L=odobroc(bloki_klucza.L);
	 bloki_klucza.P=odobroc(bloki_klucza.P);
      }

      /* Zamiana bloków danych */
      bloki.L=stare_bloki.P;
      bloki.P=stare_bloki.L ^ f(klucz,stare_bloki.P);

      stare_bloki=bloki;
   }
   /* Ostateczna zamiana bloków danych */
   bloki.L=stare_bloki.P;
   bloki.P=stare_bloki.L;
   /* Końcowe przekształcenie i odwrócenie bitów */
   return odwroc(wybierz(dlugoscPI,PI,polacz(bloki)));
}
